package com.coder4j.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 反射工具类
 *
 * @author Keller
 * @date 2023-08-10 09:16:11
 */
public class ReflectUtils {
    /**
     * 类对应的属性，用作缓存，提升操作效率
     * 类，属性名，属性
     */
    private static Map<Class, Map<String, Field>> cacheFieldMap = new ConcurrentHashMap<>();
    private static Map<Class, Map<String, Method>> cacheMethodMap = new ConcurrentHashMap<>();

    private static Map<Class,List<Field>> cacheFieldList = new HashMap<>();
    private static Map<Class,List<Method>> cacheMethodList = new HashMap<>();

    /**
     * 为类的属性设置值
     *
     * @param entity    实例对象
     * @param fieldName 属性名称
     * @param value     属性值
     * @param <T>
     * @return
     */
    public static <T> T setValue(T entity, String fieldName, Object value) {
        try {
            Class<?> aClass = entity.getClass();
            Field num = getField(aClass, fieldName);
            num.setAccessible(true);
            try {
                num.set(entity, value);
            } catch (Exception e) {
                //如果是复杂对象，不做处理
                if (!(value instanceof String)) {
                    e.printStackTrace();
                    return null;
                }
                String str = (String) value;
                Class<?> type = num.getType();
                if (type.equals(Integer.class) || type.equals(int.class)) {
                    num.set(entity, Integer.parseInt(str));
                    return entity;
                }
                if (type.equals(Long.class) || type.equals(long.class)) {
                    num.set(entity, Long.parseLong(str));
                    return entity;
                }
                if (type.equals(Byte.class) || type.equals(byte.class)) {
                    num.set(entity, Byte.parseByte(str));
                    return entity;
                }
                if (type.equals(Short.class) || type.equals(short.class)) {
                    num.set(entity, Short.parseShort(str));
                    return entity;
                }
                if (type.equals(Boolean.class) || type.equals(boolean.class)) {
                    num.set(entity, Boolean.parseBoolean(str));
                    return entity;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return entity;
    }

    /**
     * 判断类实例中有没有值
     *
     * @param entity 实例对象
     * @param <T>
     * @return
     */
    public static <T> boolean hasValue(T entity) {
        if (entity == null) {
            return false;
        }
        List<String> fields = getFieldNames(entity);
        for (String field : fields) {
            if (hasValue(entity, field)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断一个对象的指定属性有没有值
     *
     * @param entity    实体对象
     * @param fieldName 对象的属性名
     * @param <T>       实体类型
     * @return 值存在且不为null:返回true; 否则:返回false
     */
    public static <T> boolean hasValue(T entity, String fieldName) {
        try {
            if (ObjectUtils.hasEmpty(entity)) {
                return false;
            }
            Class cls = entity.getClass();
            Method method = cls.getMethod("get" + StringUtils.firstToUpper(fieldName));
            if (method.invoke(entity) == null) {
                return false;
            } else {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取属性值
     * @param entity 实例对象
     * @param fieldName 属性名
     * @param <T>
     * @return
     */
    public static <T> Object getValue(T entity, String fieldName) {
        try {
            if (ObjectUtils.hasEmpty(entity)) {
                return null;
            }
            Class cls = entity.getClass();

            Method method = getMethod(cls,"get" + StringUtils.firstToUpper(fieldName));
            return method.invoke(entity);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 获取实体类中的属性名列表
     *
     * @param entity
     * @param <T>
     * @return
     */
    public static <T> List<String> getFieldNames(T entity) {
        Class clazz = entity.getClass();
        return new ArrayList<>(getFieldMap(clazz).keySet());
    }


    /**
     * 更新缓存
     *
     * @param clazz
     */
    public static void updateCache(Class clazz) {
        if (cacheFieldMap.containsKey(clazz) && cacheMethodMap.containsKey(clazz)) {
            return;
        }
        Class tmpClass = clazz;

        List<Field> fieldList = new ArrayList<>();
        List<Method> methodList = new ArrayList<>();
        while (tmpClass != null && !tmpClass.equals(Object.class)) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(tmpClass.getDeclaredFields())));
            methodList.addAll(new ArrayList<>(Arrays.asList(tmpClass.getDeclaredMethods())));
            tmpClass = tmpClass.getSuperclass();
        }
        cacheFieldMap.put(clazz, fieldList.stream()
                .collect(Collectors.toMap(Field::getName, Function.identity(), (k1, k2) -> k2)));

        cacheMethodMap.put(clazz, methodList.stream()
                .collect(Collectors.toMap(Method::getName, Function.identity(), (k1, k2) -> k2)));
        cacheFieldList.put(clazz,fieldList);
        cacheMethodList.put(clazz,methodList);
    }

    /**
     * 获取属性
     *
     * @param clazz     类
     * @param fieldName 属性名
     * @return
     */
    public static Field getField(Class clazz, String fieldName) {
        return getFieldMap(clazz).get(fieldName);
    }

    public static Method getMethod(Class clazz, String methodName) {
        return getMethodMap(clazz).get(methodName);
    }

    /**
     * 获取属性Map
     *
     * @param clazz
     * @return k 属性名；v 属性对象
     */
    public static Map<String, Field> getFieldMap(Class clazz) {
        updateCache(clazz);
        return cacheFieldMap.get(clazz);
    }

    public static List<Field> getFieldList(Class clazz){
        updateCache(clazz);
        return cacheFieldList.get(clazz);
    }

    public static Map<String, Method> getMethodMap(Class clazz) {
        updateCache(clazz);
        return cacheMethodMap.get(clazz);
    }

    public static List<Method> getMethodList(Class clazz){
        updateCache(clazz);
        return cacheMethodList.get(clazz);
    }
    /**
     * 过滤空白字符串
     *
     * @param entity
     * @param <T>
     */
    public static <T> void filterBlank(T entity) {
        for (Field field : getFieldMap(entity.getClass()).values()) {
            if (String.class.equals(field.getType())) {
                Object value = getValue(entity, field.getName());
                if (value == null) {
                    continue;
                }
                if (StringUtils.isEmpty((String) value)) {
                    setValue(entity, field.getName(), null);
                }
            }
        }
    }
}
